package m.html;

import m.common.model.Model;
import m.common.model.util.*;
import m.html.element.Element;
import m.html.element.attr.Align;
import m.html.list.ListColumn;
import m.html.list.ListCount;
import m.html.list.ListSearch;
import m.html.list.SearchType;
import m.html.util.IVueListColumn;
import m.html.util.VueListPage;
import m.system.db.DBManager;
import m.system.db.DataRow;
import m.system.lang.HtmlBodyContent;
import m.system.util.ArrayUtil;
import m.system.util.JSONMessage;
import m.system.util.StringUtil;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VueListResult extends VueListPage<ListColumn,VueListResult> {
	private String selectField;
	private boolean isRadio=false;
	private String selectChecked="1==2";
	private final Class<?> clazz;
	private List<ListColumn> cols;
	private final List<QueryCondition> conditions;
	private final List<QueryOrder> orders;

	/**
	 * 构造方法
	 *  clazz 查询的类
	 *  dataUrl 查询数据的地址
	 *  queryData list代表查询数据,count代表记录数和汇总数据  空或其他返回页面
	 */
	public <T extends Model> VueListResult(String title,Class<T> clazz,String dataUrl,String queryData,Map<String,String> params) {
		this.title=title;
		this.subTitle="";
		this.describe="";
		this.selectField="";
		this.clazz=clazz;
		this.dataUrl=dataUrl;
		this.queryData=queryData;
		this.cols= new ArrayList<>();
		this.queryFields= new HashSet<>();
		this.searchMap= new ArrayList<>();
		this.params=params;
		this.page=new QueryPage();
		if(null==this.params) this.params= new HashMap<>();
		this.buttons= new ArrayList<>();
		this.bottomButtons=new ArrayList<>();
		this.conditions= new ArrayList<>();
		this.orders= new ArrayList<>();
		this.height=0;
		String[] urlParts=dataUrl.split("/");
		String[] actions=new String[urlParts.length-2];
		System.arraycopy(urlParts,1,actions,0,actions.length);
		this.actionName=ArrayUtil.connection(actions, "/");
	}
	/**
	 * 添加列
	 *  col
	 * 
	 */
	public VueListResult addCol(ListColumn col) {
		this.cols.add(col);
		this.queryFields.addAll(col.getParamFields());
		if(col.getFilter()) {
			if(!col.getDisItems().isEmpty()) {
				col.setFilter(SearchType.HIDDEN_EQ);
			}
			this.addSearch(
				ListSearch.field(col.getTitle(),col.getField(),col.getFilterType(),col.getDisItems())
					.setDictType(col.getDictType()).setDictTree(col.isDictTree())
					.setNotQuery(true)
			);
		}
		return this;
	}

	/**
	 * 设置单选框或多选框
	 *  field
	 *  isRadio 是否单选
	 *  checkedExp 选择js表达式  record记录行  不推荐可翻页列表
	 * 
	 */
	public VueListResult setSelection(String field,boolean isRadio,String checkedExp) {
		this.selectField=field;
		this.isRadio=isRadio;
		if(!StringUtil.isSpace(checkedExp)) this.selectChecked=checkedExp;
		return this;
	}
	/**
	 * 设置单选框或多选框
	 *  field
	 *  isRadio
	 *  可用于翻页列表多选保留
	 */
	public VueListResult setSelection(String field,boolean isRadio) {
		return setSelection(field, isRadio, null);
	}

	/**
	 * 添加查询数据时的默认查询条件
	 *  cond
	 * 
	 */
	public VueListResult addCondition(QueryCondition... cond) {
		this.conditions.addAll(Arrays.asList(cond));
		return this;
	}
	public VueListResult addOrder(QueryOrder... order) {
		this.orders.addAll(Arrays.asList(order));
		return this;
	}

	/**
	 * list：列表   count：行数，合计
	 */
	protected JSONMessage getQueryResult(String type) throws Exception {
		JSONMessage result=new JSONMessage();
		result.push("code", 0);
		result.push("asMap", new HashMap<String,String>());
		if(null!=columnsParam) {
			List<ListColumn> newList = new ArrayList<>();
			for (String[] arr : columnsParam) {
				for (ListColumn col : cols) {
					if (arr[0].equals(ArrayUtil.isEmpty(col.getGroupTitle()) ? col.getTitle() : col.getGroupTitle()[0])) {
						newList.add(col);
						if(ArrayUtil.isEmpty(col.getGroupTitle())) break;
					}
				}
			}
			this.cols = newList;
		}
		if(type.contains("list")) {
			int sc=this.scol;
			int sl=this.slength;
			List<String> colSb = new ArrayList<>();
			for (int i = 0; i < this.cols.size(); i++) {
				ListColumn col = this.cols.get(i);
				if (!ArrayUtil.isEmpty(col.getGroupTitle())) {
					String json = toTitleJson(col, i == this.cols.size() - 1);
					if (!StringUtil.isSpace(json)) {
						colSb.add(json);
					}
				} else {
					String json = toTitleJson();
					if (!StringUtil.isSpace(json)) {
						colSb.add(json);
					}
					colSb.add(col.toJsonString());
				}
			}
			String last = toLastJson();
			if (null != last) colSb.add(last);
			List<JSONMessage> cs=resetColumns(colSb);
			for(JSONMessage jm : cs){
				for(IVueListColumn col : cols){
					if(jm.get("title").equals(ArrayUtil.isEmpty(col.getGroupTitle())?col.getTitle():col.getGroupTitle()[0])
							&&col.getFixed()== Align.NONE){
						jm.push("sort","Y");
					}
				}
			}
			result.push("columnsSetting",cs);
			result.push("columns", colSb.stream().map(HtmlBodyContent::new).toArray());
			result.push("scol",sc);
			result.push("slength",sl);
		}
		for(ListSearch ls : this.searchMap) {
			if(ls.isField()) {
				this.conditions.addAll(ls.toCondition(this.params));
			}
		}
		if(StringUtil.isSpace(this.selectField)) this.queryFields.add(this.selectField);
		Map<String,String> expMap= new HashMap<>();
		Map<String,ListCount> sumMap= new LinkedHashMap<>();
		Map<String,String> sumExpMap=new HashMap<>();
		for(ListColumn col : this.cols) {
			if(!isShowCol(col)) continue;
			String exp=col.getFieldExpression();
			if(!StringUtil.isSpace(exp)) expMap.put(col.getField(), exp);
			String k=col.getField();
			String countFieldExp=col.getCountFieldExpression();
			if(col.getCount()!=ListCount.NONE||null!=countFieldExp){
				sumMap.put(k,col.getCount());
				sumExpMap.put(k,countFieldExp);
			}
		}
		if(type.contains("list")){
			this.orders.add(0, this.order);
			result.push("data",
				ModelQueryList.getModelList((Class<Model>)this.clazz,
					this.queryFields.toArray(new String[] {}),
					this.page,
					QueryCondition.and(this.conditions.toArray(new QueryCondition[] {})),
					expMap,
					this.orders.toArray(new QueryOrder[] {})
				)
			);
		}
		if(type.contains("count")){
			//填充合计行
			if(!sumMap.isEmpty()){
				ModelQueryList query=ModelQueryList.instance((Class<Model>)this.clazz,
						this.queryFields.toArray(new String[] {}),
						null,
						QueryCondition.and(this.conditions.toArray(new QueryCondition[] {}))
				);
				QueryParameter qp=query.getQueryParameter();
				StringBuilder sql=new StringBuilder("SELECT count(*) count_num");
				for(String k : sumMap.keySet()){
					String exp=sumExpMap.get(k);
					if(null!=exp){
						Pattern pattern=Pattern.compile("\\#\\{.+?\\}");
						Matcher matcher=pattern.matcher(exp);
						while(matcher.find()) {
							String str=matcher.group();
							String ns=str.substring(2,str.length()-1);
							exp=exp.replace(str, "a."+query.getAlias4Field(ns));
						}
						sql.append(",").append(exp).append(" ");
						sql.append(k);
					}else {
						sql.append(",").append(sumMap.get(k));
						sql.append("(a.").append(query.getAlias4Field(k)).append(") ");
						sql.append(k.replaceAll("\\.", "_"));
					}
				}
				sql.append(" FROM (").append(qp.getSql()).append(") a");
				DataRow dr=DBManager.queryFirstRow(sql.toString(),qp.getValueList().toArray(new Object[]{}));
				if(null!=dr){
					List<Object[]> ls= new ArrayList<>();
					if(!StringUtil.isSpace(this.selectField)) {
						ls.add(new Object[] {"","","","left","",true});
					}
					for(ListColumn col : this.cols) {
						if(!isShowCol(col)) continue;
						Object n=dr.get(col.getField().replaceAll("\\.","_"));
						if(null!=n) {
							ls.add(new Object[] {n,col.getNumberFormat()+col.getDateFormat(),col.getAlign().toString(),col.getFixed().toString(),col.getType(),col.isFalse()});
						}else {
							ls.add(new Object[] {"","","",col.getFixed().toString(), col.getType(),col.isFalse()});
						}
					}
					result.push("count", dr.get(Long.class,"count_num").intValue());
					result.push("summary", ls);
				}
			}else {
				ModelQueryList mq=ModelQueryList.instance((Class<Model>)this.clazz,
						this.queryFields.toArray(new String[] {}),
						this.page,
						QueryCondition.and(this.conditions.toArray(new QueryCondition[] {})),
						expMap,
						this.orders.toArray(new QueryOrder[] {})
				);
				DataRow ds=DBManager.executeQuery(mq.getCountParameter()).rows().get(0);
				int count=0;
				if(null!=ds){
					count=ds.get(Long.class, "num").intValue();
				}
				result.push("count", count);
				result.push("summary", new ArrayList<>());
			}
		}
		return result;
	}
	protected Element getTableElement(){
		return Element.tag("a-table").A(":custom-row","customRow").A("ref", "vueListResultTable").A(":sticky","{getContainer:stickyGetContainer,offsetHeader:-1}")
		.A(":row-selection",StringUtil.isSpace(this.selectField)?"null":"{fixed:true,type:'"+(this.isRadio?"radio":"checkbox")+"',selectedRowKeys:selectedRowKeys,onChange:selectChange}")
		.A(":row-key","selectField").A(":scroll", "{x:width,y:bodyHeight}")
		.A("size","small").A(":columns","columns").A(":data-source","tableData").A("@change","tableChange")
		.A(":pagination","false").A("tableLayout","fixed").A("@resizeColumn","handleResizeColumn")
		.A(":loading","tableLoad");
	}

	@Override
	protected String getVueDataPart() {
		return "selectField:'"+this.selectField+"'," +
		"selectedRowKeys:[]," +
		"selectedRowObjs:{}";
	}

	@Override
	protected String getVueMethodPart() {
		return "customRow:function(record,index){" +
		" return {onDblclick:(event)=>{this.setSelected(index,record,true);}};" +
		"}," +
		"getSelectFieldValue:function(record){" +
		" return record." + (StringUtil.isSpace(this.selectField) ? "x" : this.selectField) + ";" +
		"}," +
		"selectChange:function(srk, sr){console.log(srk, sr);" +
		" if(srk.length==0){" +
		"  for(let i=0;i<this.tableData.length;i++){" +
		"   if(this.selectedRowKeys.indexOf(this.getSelectFieldValue(this.tableData[i]))>=0)" +
		"    this.setSelected(0,this.tableData[i],true);" +
		"  }" +
		"  return;" +
		" }" +
		" for(let i=0;i<srk.length;i++){" +
		"  if(this.selectedRowKeys.indexOf(srk[i])==-1) this.setSelected(0,sr[i],true);" +
		" }" +
		" for(let i=0;i<this.selectedRowKeys.length;i++){" +
		"  if(srk.indexOf(this.selectedRowKeys[i])==-1) this.setSelected(0,this.selectedRowObjs[this.selectedRowKeys[i]],true);" +
		" }" +
		"}," +
		"setSelected:function(index,record,flag){" +
		" if(" + this.selectChecked + "||flag){" +
		(isRadio ? "this.selectedRowKeys=[];this.selectedRowObjs={};" : "") +
		"  let value=record[this.selectField];" +
		"  if(this.selectedRowKeys.indexOf(value)>-1){" +
		"   this.selectedRowKeys.remove(value);" +
		"   delete this.selectedRowObjs[value];" +
		"  }else{" +
		"   this.selectedRowKeys.push(value);" +
		"   this.selectedRowObjs[value]=record;" +
		"  }" +
		"  this.notify(true,'listResult,queryData',{selectedRowKeys:this.selectedRowKeys,selectedRowObjs:this.selectedRowObjs});" +
		" }" +
		"}," +
		"concatSelected:function(fn){" +
		" let str='';" +
		" for(let i=0;i<this.selectedRowKeys.length;i++){" +
		"  if(i!=0) str+=',';" +
		"  str+=fn(this.selectedRowObjs[this.selectedRowKeys[i]]);" +
		" }" +
		" return str;" +
		"}," +
		"sumSelected:function(fn){" +
		" let d=0;" +
		" for(let i=0;i<this.selectedRowKeys.length;i++){" +
		"  d+=fn(this.selectedRowObjs[this.selectedRowKeys[i]])||0;" +
		" }" +
		" return d;" +
		"},";
	}

	private JSONMessage jtemp=null;
	private String toTitleJson() {
		if(null!=jtemp) {
			String str=jtemp.toJSONString();
			jtemp=null;
			return str;
		}
		return null;
	}
	private String toTitleJson(ListColumn col,boolean isLast) {
		String[] strs=col.getGroupTitle();
		if(null==jtemp) {
			jtemp=new JSONMessage();
			jtemp.push("title", strs[0]);
			jtemp.push("children", new ArrayList<>());
		}
		if(jtemp.get("title").equals(strs[0])) {
			JSONMessage jt=jtemp;
			for(int i=1;i<strs.length;i++) {
				String s2=strs[i];
				List<Object> chr=(List<Object>)jt.get("children");
				for(Object ch : chr) {
					if(ch instanceof JSONMessage) {
						JSONMessage jm=(JSONMessage)ch;
						if(jm.get("title").equals(s2)) {
							jt=jm;
						}
					}
				}
				if(!jt.get("title").equals(s2)) {
					jt=new JSONMessage();
					jt.push("title", s2);
					jt.push("children", new ArrayList<>());
					chr.add(jt);
				}
			}
			((List<Object>)jt.get("children")).add(new HtmlBodyContent(col.toJsonString()));
			if(isLast) {
				String r=jtemp.toJSONString();
				jtemp=null;
				return r;
			}
		}else {
			String r=toTitleJson();
			if(isLast) {
				return r+","+toTitleJson(col, isLast);
			}else {
				toTitleJson(col,isLast);
				return r;
			}
		}
		return null;
	}
	private String toLastJson() {
		if(null!=jtemp) return jtemp.toJSONString();
		else return null;
	}

	@Override
	protected List<ListColumn> getListCols() {
		return this.cols;
	}
}
